package com.bsj.power.pls.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsj.power.common.config.exception.ApiException;
import com.bsj.power.common.config.global.JsonResult;
import com.bsj.power.common.config.global.JsonResultEnum;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.def.entity.cardReport.CardReport;
import com.bsj.power.common.def.entity.personnel.Personnel;
import com.bsj.power.common.def.entity.watch.KeepWatch;
import com.bsj.power.common.def.entity.watch.WatchPersonnel;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagPosVO;
import com.bsj.power.common.def.vo.pls.httpVo.SimplifyPos;
import com.bsj.power.common.def.vo.pls.watch.*;
import com.bsj.power.common.mapper.CardReportMapper;
import com.bsj.power.common.mapper.KeepWatchMapper;
import com.bsj.power.common.mapper.PersonnelMapper;
import com.bsj.power.common.mapper.WatchPersonnelMapper;
import com.bsj.power.common.util.PosUtil;
import com.bsj.power.pls.config.cached.HighRiseCached;
import com.bsj.power.pls.file.uwbData.WRFileBaseUntil;
import com.bsj.power.pls.service.KeepWatchService;
import com.bsj.tools.DateUntil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author : zhuojie.c
 * @version V1.0
 * @Description: TODO
 * @date Date : 2024年08月28日
 */
@Slf4j
@Service
public class KeepWatchServiceImpl implements KeepWatchService {

    @Resource
    private PersonnelMapper personnelMapper;

    @Resource
    private CardReportMapper cardReportMapper;

    @Resource
    private KeepWatchMapper keepWatchMapper;

    @Resource
    private WatchPersonnelMapper watchPersonnelMapper;

    @Autowired
    private WRFileBaseUntil wrFileBaseUntil;

    @Override
    public JsonResult add(IKeepWatchAddVO keepWatchAddVO) {
        KeepWatch keepWatch = new KeepWatch();
        BeanUtils.copyProperties(keepWatchAddVO, keepWatch);
        if (keepWatchAddVO.getStartTime() != null) {
            keepWatch.setStartTime(DateUntil.strToDate(keepWatchAddVO.getStartTime()));
        }
        if (keepWatchAddVO.getEndTime() != null) {
            keepWatch.setEndTime(DateUntil.strToDate(keepWatchAddVO.getEndTime()));
        }
        keepWatch.setStatus(NumericalConstants.ONE);
        if (keepWatch.getRouteRate() == null) {
            keepWatch.setRouteRate(0.0);
        }
        if (keepWatch.getAreaRate() == null) {
            keepWatch.setAreaRate(0.0);
        }
        if (keepWatchMapper.insert(keepWatch) > 0) {
            if (keepWatchAddVO.getPersonnelIds() != null) {
                QueryWrapper<Personnel> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().in(Personnel::getPersonnelId, keepWatchAddVO.getPersonnelIds().stream().distinct().collect(Collectors.toList()));
                List<Personnel> list = personnelMapper.selectList(queryWrapper);
                List<Integer> personnelIdList = list.stream().map(Personnel::getPersonnelId).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(personnelIdList)) {
                    personnelIdList.forEach(v -> {
                        watchPersonnelMapper.insert(new WatchPersonnel(keepWatch.getId(), v));
                    });
                }
            }
        }
        return JsonResult.success(keepWatch);
    }

    @Override
    public JsonResult update(IKeepWatchUpdateVO keepWatchUpdateVO) {
        KeepWatch keepWatch = keepWatchMapper.selectById(keepWatchUpdateVO.getId());
        if (keepWatch == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        BeanUtils.copyProperties(keepWatchUpdateVO, keepWatch);
        if (keepWatchUpdateVO.getStartTime() != null) {
            keepWatch.setStartTime(DateUntil.strToDate(keepWatchUpdateVO.getStartTime()));
        }
        if (keepWatchUpdateVO.getEndTime() != null) {
            keepWatch.setEndTime(DateUntil.strToDate(keepWatchUpdateVO.getEndTime()));
        }
        if (keepWatch.getRouteRate() == null) {
            keepWatch.setRouteRate(0.0);
        }
        if (keepWatch.getAreaRate() == null) {
            keepWatch.setAreaRate(0.0);
        }
        if (keepWatchMapper.updateById(keepWatch) > 0) {
            if (keepWatchUpdateVO.getPersonnelIds() != null) {
                QueryWrapper<Personnel> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().in(Personnel::getPersonnelId, keepWatchUpdateVO.getPersonnelIds().stream().distinct().collect(Collectors.toList()));
                List<Personnel> list = personnelMapper.selectList(queryWrapper);
                List<Integer> personnelIdList = list.stream().map(Personnel::getPersonnelId).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(personnelIdList)) {
                    // 删除旧数据
                    watchPersonnelMapper.delete(new QueryWrapper<WatchPersonnel>().eq("watch_id", keepWatch.getId()));
                    // 重新插入
                    personnelIdList.forEach(v -> {
                        watchPersonnelMapper.insert(new WatchPersonnel(keepWatch.getId(), v));
                    });
                }
            }
        }
        return JsonResult.success(keepWatch);
    }

    @Override
    public JsonResult changeStatus(IWatchChangeStatusVO watchChangeStatusVO) {
        KeepWatch keepWatch = keepWatchMapper.selectById(watchChangeStatusVO.getId());
        if (keepWatch == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        if (watchChangeStatusVO.getStatus().equals(NumericalConstants.ONE)) {
            keepWatch.setRouteRate(0.0);
            keepWatch.setAreaRate(0.0);
        } else if (watchChangeStatusVO.getStatus().equals(NumericalConstants.TWO)) {
            keepWatch.setRouteRate(50.0);
            keepWatch.setAreaRate(50.0);
        } else if (watchChangeStatusVO.getStatus().equals(NumericalConstants.THREE)) {
            keepWatch.setRouteRate(100.0);
            keepWatch.setAreaRate(100.0);
        }
        keepWatch.setStatus(watchChangeStatusVO.getStatus());
        if (keepWatchMapper.updateById(keepWatch) > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    @Override
    public JsonResult delete(Long id) {
        keepWatchMapper.deleteById(id);
        watchPersonnelMapper.delete(new QueryWrapper<WatchPersonnel>().eq("watch_id", id));
        return JsonResult.success();
    }

    @Override
    public JsonResult<List<OKeepWatchPageVO>> page(IKeepWatchSearchVO keepWatchSearchVO) {
        IPage<KeepWatch> page = new Page<>(keepWatchSearchVO.getPageNumber(), keepWatchSearchVO.getPageSize());
        QueryWrapper<KeepWatch> queryWrapper = new QueryWrapper<>();
        if (keepWatchSearchVO.getStartTime() != null && keepWatchSearchVO.getEndTime() != null) {
            queryWrapper.lambda().between(KeepWatch::getStartTime, keepWatchSearchVO.getStartTime(), keepWatchSearchVO.getEndTime());
        }
        if (keepWatchSearchVO.getStatus() != null && keepWatchSearchVO.getStatus() != 0) {
            queryWrapper.lambda().eq(KeepWatch::getStatus, keepWatchSearchVO.getStatus());
        }
        queryWrapper.lambda().orderByDesc(KeepWatch::getCreateTime);
        IPage<KeepWatch> iPage = keepWatchMapper.selectPage(page, queryWrapper);
        List<KeepWatch> records = iPage.getRecords();
        List<OKeepWatchPageVO> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(records)) {
            records.forEach(v -> {
                OKeepWatchPageVO keepWatchPageVO = new OKeepWatchPageVO();
                BeanUtils.copyProperties(v, keepWatchPageVO);
                List<OWatchPersonnelVO> personnelLists = watchPersonnelMapper.selectPersonnelByWatchId(v.getId());
                if (CollectionUtil.isNotEmpty(personnelLists)) {
                    keepWatchPageVO.setPersonnelIds(personnelLists.stream().mapToLong(OWatchPersonnelVO::getPersonnelId).boxed().collect(Collectors.toList()));
                    keepWatchPageVO.setPersonnelNames(personnelLists.stream().map(OWatchPersonnelVO::getPersonnelName).collect(Collectors.toList()));
                    keepWatchPageVO.setPersonnelEmpNos(personnelLists.stream().map(OWatchPersonnelVO::getEmpNo).collect(Collectors.toList()));
                }
                list.add(keepWatchPageVO);
            });
        }
        return JsonResult.success(list, iPage.getTotal());
    }

    @Override
    public JsonResult<OKeepWatchDetail> detail(Long id) {
        KeepWatch keepWatch = keepWatchMapper.selectById(id);
        if (keepWatch == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        OKeepWatchDetail keepWatchDetail = new OKeepWatchDetail();
        BeanUtils.copyProperties(keepWatch, keepWatchDetail);
        List<OWatchPersonnelVO> personnelLists = watchPersonnelMapper.selectPersonnelByWatchId(keepWatch.getId());
        if (CollectionUtil.isNotEmpty(personnelLists)) {
            keepWatchDetail.setPersonnelIds(personnelLists.stream().mapToLong(OWatchPersonnelVO::getPersonnelId).boxed().collect(Collectors.toList()));
            keepWatchDetail.setPersonnelNames(personnelLists.stream().map(OWatchPersonnelVO::getPersonnelName).collect(Collectors.toList()));
            keepWatchDetail.setPersonnelEmpNos(personnelLists.stream().map(OWatchPersonnelVO::getEmpNo).collect(Collectors.toList()));
        }
        return JsonResult.success(keepWatchDetail);
    }

    /**
     * 计算达标率
     * @param id
     * @return
     */
    @Override
    public JsonResult reachPos(Long id) {
        KeepWatch keepWatch = keepWatchMapper.selectById(id);
        if (keepWatch == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        if (keepWatch.getBuilding() == null || keepWatch.getFloor() == null) {
            throw new ApiException(JsonResultEnum.POS_ROAD_IS_NULL);
        }
        //计算巡更完成率跟达标率
        List<WatchPersonnel> watchPersonnels = watchPersonnelMapper.selectList(Wrappers.<WatchPersonnel>lambdaQuery()
                .eq(WatchPersonnel::getWatchId, keepWatch.getId()));
        KeepWatch kw = new KeepWatch();
        kw.setId(id);
        if (!CollectionUtils.isEmpty(watchPersonnels)) {
            //到达人数
            AtomicInteger arriveIn = new AtomicInteger(0);
            //达标人数
            AtomicInteger passIn = new AtomicInteger(0);
            watchPersonnels.stream().forEach(item -> {
                Personnel personnel = personnelMapper.selectById(item.getPersonnelId());
                if (personnel == null) {
                    return;
                }
                String deviceName = null;
                //获取绑卡记录
                CardReport cardReport = cardReportMapper.selectOne(Wrappers.<CardReport>lambdaQuery()
                        .eq(CardReport::getPersonnelId, personnel.getPersonnelId())
                        .eq(CardReport::getEmpNo, personnel.getEmpNo())
                        .orderByDesc(CardReport::getCreateTime).last("limit 1"));
                if (cardReport == null) {
                    return;
                }
                if (cardReport.getType() == 1) {
                    //最新的数据是绑卡,先判断该绑卡时间是否符合给定的时间范围
                    if (cardReport.getCreateTime().getTime() <= keepWatch.getStartTime().getTime()
                            && cardReport.getCreateTime().getTime() >= keepWatch.getEndTime().getTime()) {
                        //证明绑卡在有效时间内
                        deviceName = cardReport.getDeviceName();
                    }
                }
                if (deviceName == null) {
                    CardReport cardReportLimit = cardReportMapper.selectOne(Wrappers.<CardReport>lambdaQuery()
                            .eq(CardReport::getPersonnelId, personnel.getPersonnelId())
                            .eq(CardReport::getEmpNo, personnel.getEmpNo())
                            .eq(CardReport::getType, 1)
                            .le(CardReport::getCreateTime, keepWatch.getEndTime())
                            .orderByDesc(CardReport::getCreateTime).last("limit 1"));
//                    List<CardReport> cardReports = cardReportMapper.selectList(Wrappers.<CardReport>lambdaQuery()
//                            .eq(CardReport::getPersonnelId, personnel.getPersonnelId())
//                            .eq(CardReport::getEmpNo, personnel.getEmpNo())
//                            .between(CardReport::getCreateTime, keepWatch.getStartTime(), keepWatch.getEndTime()));
//                    if (CollectionUtils.isEmpty(cardReports)) {
//                        //TODO
//                        return;
//                    }
//                    deviceName = cardReports.get(0).getDeviceName();
                    if (cardReportLimit == null) {
                        return;
                    }
                    deviceName = cardReportLimit.getDeviceName();
                }
                List<byte[]> listBytes = wrFileBaseUntil.readDataBytes(deviceName, keepWatch.getStartTime().getTime(), keepWatch.getEndTime().getTime());
                if (CollectionUtils.isEmpty(listBytes)) {
                    //TODO
                    return;
                }
                List<SimplifyPos> simplifyPosList = new ArrayList<>();
                for (byte[] bytes : listBytes) {
                    String data = new String(bytes);
                    HttpTagPosVO posData = JSON.parseObject(data, HttpTagPosVO.class);
                    //x,y,z
                    double[] pos = posData.getPos();
                    if (pos.length < 3) {
                        continue;
                    }
                    SimplifyPos simplifyPos = SimplifyPos.builder().build();
                    simplifyPos.sp(posData);
                    simplifyPosList.add(simplifyPos);
                }
                ArrayList<SimplifyPos> simplifyPosArrayList = simplifyPosList.stream().collect(Collectors.collectingAndThen(
                        Collectors.toMap(SimplifyPos::getPosStr, Function.identity(), (existing, replacement) -> existing),
                        map -> new ArrayList<>(map.values())));
                int pass = 0;
                boolean arrive = false;
                for (SimplifyPos spPos : simplifyPosArrayList) {
                    double[] reducePos = spPos.getReducePos();
                    Integer pathMapId = HighRiseCached.getFloorByMapId(keepWatch.getBuilding(), keepWatch.getFloor());
                    boolean inRoad = PosUtil.posInRoad(keepWatch.getRoutePlanning(), pathMapId,
                            5D, reducePos[0], -reducePos[1], spPos.getMapId());
                    if (pathMapId.equals(spPos.getMapId())) {
                        //只要到了一次就算到达
                        arrive = true;
                    }
                    if (inRoad) {
                        //达标
                        pass += 1;
                    }
                }
                //有效位置点数量
                int posSize = simplifyPosArrayList.size();
                double posPass = NumberUtil.div(pass, posSize, 2);
                if (posPass > 0.5) {
                    //超过百分之50就算达标
                    passIn.addAndGet(1);
                }
                if (arrive) {
                    //到达人数 + 1
                    arriveIn.addAndGet(1);
                }
            });
            int size = watchPersonnels.size();
            double passDiv = NumberUtil.div(passIn.get(), size, 2);
            double arriveDiv = NumberUtil.div(arriveIn.get(), size, 2);
            kw.setRouteRate(NumberUtil.mul(passDiv, 100D));
            kw.setAreaRate(NumberUtil.mul(arriveDiv, 100D));
            kw.setStatus(3);
            keepWatchMapper.updateById(kw);
        }
        return JsonResult.success(kw);
    }


}
